/*
 * Copyright (c) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


import { ArkMethod, ArkFile, ArkAssignStmt, Stmt } from "arkanalyzer/lib";
import Logger, { LOG_MODULE_TYPE } from 'arkanalyzer/lib/utils/logger';
import { BaseChecker, BaseMetaData } from "../BaseChecker";
import { Defects, IssueReport } from "../../model/Defects";
import { ClassMatcher, FileMatcher, MatcherCallback, MatcherTypes, MethodMatcher } from "../../matcher/Matchers";
import { Rule } from "../../model/Rule";
import { RuleListUtil } from "../../utils/common/DefectsList";
const defaultOptions: Options = {
    allowAny: false,
    allowBoolean: false,
    allowNever: false,
    allowNullish: false,
    allowNumber: false,
    allowRegExp: false,
}
interface Options {
    /** Whether to allow `any` typed values in template expressions. */
    allowAny?: boolean;
    /** Whether to allow `boolean` typed values in template expressions. */
    allowBoolean?: boolean;
    /** Whether to allow `never` typed values in template expressions. */
    allowNever?: boolean;
    /** Whether to allow `nullish` typed values in template expressions. */
    allowNullish?: boolean;
    /** Whether to allow `number` typed values in template expressions. */
    allowNumber?: boolean;
    /** Whether to allow `regexp` typed values in template expressions. */
    allowRegExp?: boolean;
}
const logger = Logger.getLogger(LOG_MODULE_TYPE.HOMECHECK, 'RestrictTemplateExpressionsCheck');
const gmetaData: BaseMetaData = {
    severity: 2,
    ruleDocPath: "docs/restrict-template-expressions-check.md", // TODO: support url
    description: "Enforce template literal expressions to be of string type."
};
export class RestrictTemplateExpressionsCheck implements BaseChecker {
    readonly metaData: BaseMetaData = gmetaData;

    public rule: Rule;
    public defects: Defects[] = [];
    public issues: IssueReport[] = [];
    private fileMatcher: FileMatcher = {
        matcherType: MatcherTypes.FILE,
    };
    private classMatcher: ClassMatcher = {
        file: [this.fileMatcher],
        matcherType: MatcherTypes.CLASS
    };
    private methodMatcher: MethodMatcher = {
        matcherType: MatcherTypes.METHOD,
        class: [this.classMatcher]
    };
    public registerMatchers(): MatcherCallback[] {
        const matchBuildCb: MatcherCallback = {
            matcher: this.methodMatcher,
            callback: this.check
        }
        return [matchBuildCb];
    }
    public check = (method: ArkMethod) => {
        // 将规则选项转换为Options类型
        let options: Options;
        if (this.rule && this.rule.option.length > 0) {
            options = this.rule.option[0] as Options;
        } else {
            options = defaultOptions;
        }
        let targetVariableNames: string[] = [];
        if (method instanceof ArkMethod) {
            let declareClass = method.getDeclaringArkClass()
            const arkFile = declareClass.getDeclaringArkFile();
            for (let stmt of method.getBody()?.getCfg().getStmts() ?? []) {
                // 获取语句的原始文本
                let text = stmt.getOriginalText() ?? '';
                // 如果语句的原始文本为空，直接跳过
                if (!text) {
                    continue;
                }
                // 如果语句不是ArkAssignStmt类型的，则跳过
                if (!(stmt instanceof ArkAssignStmt)) {
                    continue;
                }
                // 获取赋值语句的右侧操作数
                let rightOp = stmt.getRightOp();
                if (!rightOp) {
                    continue;
                }
                let type = rightOp.getType();
                let typeString = type.getTypeString();
                // 通用检查类型，检查变量的类型
                this.generalInspectionType(stmt, typeString, targetVariableNames, options, text, arkFile);
                // 如果文本匹配模板字符串，并且目标变量名数组长度大于0
                if (this.matchTemplateString(text) && targetVariableNames.length > 0) {
                    // 遍历目标变量名数组
                    for (let targetVariableName of targetVariableNames) {
                        // 将右侧操作数转换为字符串
                        let rightOp = stmt.getRightOp().toString();
                        // 如果右侧操作数包含目标变量名
                        if (rightOp.includes(targetVariableName)) {
                            // 匹配模板字符串包括变量名，例如`${target}`
                            const regex = /\$\{([^}]+)\}/g;
                            let matchText = text.match(regex);
                            if (matchText) {
                                // 获取第一个匹配的模板字符串，如果有分号则去掉分号
                                let target = matchText[0].split(';')?.[0];
                                const regex = /\w+\s*\|\|\s*'[^']+'/;
                                if (rightOp.includes(targetVariableName) && target.match(regex)) {
                                    this.addIssueReport(arkFile, target, text);
                                }
                                this.addIssueReport(arkFile, target, text);
                            }
                        }
                    }
                }
            }
        }
    }
    private generalInspectionType(stmt: Stmt, typeString: string, targetVariableNames: string[], options: Options, text?: string, arkFile?: ArkFile) {
        //所有选项都为false时，只允许string类型。
        if (options.allowAny == false && options.allowBoolean == false && options.allowNullish == false && options.allowNumber == false && options.allowRegExp == false && options.allowNever == false) {
            // 如果操作数类型不是字符串，则将左侧操作数转换为字符串并添加到目标变量名数组中
            if (typeString !== 'string') {
                if (stmt instanceof ArkAssignStmt) {
                    let leftOp = stmt.getLeftOp().toString();
                    const regex = /%/g;
                    const match = leftOp.match(regex);
                    if (!match) {
                        targetVariableNames.push(leftOp);
                    }
                }
            }
            //添加包含三元表达式的特殊情况
            const textCheck = text ?? '';
            if (typeString == 'string' && this.TernaryExpression(textCheck)) {
                if (stmt instanceof ArkAssignStmt) {
                    // 匹配模板字符串包括变量名，例如`${target}`
                    const regex = /\$\{([^}]+)\}/g;
                    let matchText = textCheck.match(regex);
                    if (matchText) {
                        // 获取第一个匹配的模板字符串，如果有分号则去掉分号
                        let target = matchText[0].split(';')?.[0];
                        const regex = /\w+\s*\|\|\s*'[^']+'/;
                        if (arkFile) {
                            this.addIssueReport(arkFile, target, textCheck);
                        }
                    }
                }
            }
        }
        //只有allowNumber为true时，允许number和string类型。
        if (options.allowNumber == true && options.allowAny == false && options.allowBoolean == false && options.allowNullish == false && options.allowRegExp == false && options.allowNever == false) {
            if (typeString !== 'string' && typeString !== 'number') {
                if (stmt instanceof ArkAssignStmt) {
                    let leftOp = stmt.getLeftOp().toString();
                    const regex = /%/g;
                    const match = leftOp.match(regex);
                    if (!match) {
                        targetVariableNames.push(leftOp);
                    }
                }
            }
        }
        //只有allowBoolean为true时，允许boolean和string类型。
        if (options.allowBoolean == true && options.allowAny == false && options.allowNumber == false && options.allowNullish == false && options.allowRegExp == false && options.allowNever == false) {
            if (typeString !== 'string' && typeString !== 'boolean') {
                if (stmt instanceof ArkAssignStmt) {
                    let leftOp = stmt.getLeftOp().toString();
                    const regex = /%/g;
                    const match = leftOp.match(regex);
                    if (!match) {
                        targetVariableNames.push(leftOp);
                    }
                }
            }
        }
        //只有allowAny为true时，允许unknown和string类型。
        if (options.allowAny == true && options.allowBoolean == false && options.allowNumber == false && options.allowNullish == false && options.allowRegExp == false && options.allowNever == false) {
            if (typeString !== 'string' && typeString !== 'unknown') {
                if (stmt instanceof ArkAssignStmt) {
                    let leftOp = stmt.getLeftOp().toString();
                    const regex = /%/g;
                    const match = leftOp.match(regex);
                    if (!match) {
                        targetVariableNames.push(leftOp);
                    }
                }
            }
        }
        //只有allowNullish为true时，允许null和string类型。
        if (options.allowNullish == true && options.allowBoolean == false && options.allowNumber == false && options.allowAny == false && options.allowRegExp == false && options.allowNever == false) {
            if (typeString !== 'string' && typeString !== 'null') {
                if (stmt instanceof ArkAssignStmt) {
                    let leftOp = stmt.getLeftOp().toString();
                    const regex = /%/g;
                    const match = leftOp.match(regex);
                    if (!match) {
                        targetVariableNames.push(leftOp);
                    }
                }
            }
        }
        //只有allowRegExp为true时，允许RegExp和string类型。
        if (options.allowRegExp == true && options.allowBoolean == false && options.allowNumber == false && options.allowAny == false && options.allowNullish == false && options.allowNever == false) {
            if (typeString !== 'string' && !this.matchRegExp(typeString)) {
                if (stmt instanceof ArkAssignStmt) {
                    let leftOp = stmt.getLeftOp().toString();
                    const regex = /%/g;
                    const match = leftOp.match(regex);
                    if (!match) {
                        targetVariableNames.push(leftOp);
                    }
                }
            }
        }
        //只有allowNever为true时，允许三元表达式和string类型。
        if (options.allowNever == true && options.allowBoolean == false && options.allowNumber == false && options.allowAny == false && options.allowNullish == false && options.allowRegExp == false) {
            const textCheck = text ?? ''
            if (typeString !== 'string' && !this.TernaryExpression(textCheck)) {
                if (stmt instanceof ArkAssignStmt) {
                    let leftOp = stmt.getLeftOp().toString();
                    const regex = /%/g;
                    const match = leftOp.match(regex);
                    if (!match) {
                        targetVariableNames.push(leftOp);
                    }
                }
            }
        }
    }
    // 判断文本中是否包含三元表达式
    private TernaryExpression(text: string) {
        return /arg\s*===\s*'string'\s*\?\s*arg\s*:/g.test(text);
    }
    // 判断文本中是否包含模板字符串，例如`${}`
    private matchTemplateString(text: string) {
        return /\$\{(.*?)\}/g.test(text);
    }
    private matchRegExp(text: string) {
        return /\bRegExp\b/.test(text);
    }
    private addIssueReport(arkFile: ArkFile, name: string, text: string) {
        const severity = this.rule.alert ?? this.metaData.severity;
        const warnInfo = this.getLineAndColumn(arkFile, name, text);
        this.metaData.description = this.metaData.description.padEnd(130);
        const filePath = arkFile.getFilePath();
        let defects = new Defects(warnInfo.line, warnInfo.startCol, warnInfo.endCol, this.metaData.description, severity, this.rule.ruleId, filePath,
            this.metaData.ruleDocPath, true, false, false);
        this.issues.push(new IssueReport(defects, undefined));
        RuleListUtil.push(defects);
    }
    private getLineAndColumn(arkfile: ArkFile, name: string, text: string) {
        if (arkfile) {
            const classes = arkfile.getClasses();
            const code = arkfile.getCode() ?? '';
            const lines = code.split('\r\n');
            let lineNumber = 1;
            for (const line of lines) {
                const textLineIndex = line.indexOf(text);
                if (textLineIndex !== -1) {
                    const nameLineIndex = line.indexOf(name);
                    if (nameLineIndex !== -1) {
                        // 列号从1开始,此处假定模板字符串的格式为`${}`，因此列号偏移3个字符位置
                        const startCol = nameLineIndex + 3;
                        const endCol = nameLineIndex + 3;
                        const originPath = arkfile.getFilePath();
                        return { line: lineNumber, startCol, endCol, filePath: originPath }
                    }
                }
                lineNumber++;
            }
        } else {
            logger.debug('arkFile is null');
        }
        return { line: -1, startCol: -1, endCol: -1, filePath: '' };
    }
}